package com.apestech.framework.stream;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

import com.apestech.formwork.stream.ListStream;
import com.apestech.framework.file.BufferFileRead;
import com.apestech.framework.file.FileDictionary.LogSaveType;
import com.apestech.framework.file.callback.ReadBufferFileCallBackHandler;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.json.JSONSerialization;
import com.apestech.framework.run.callback.ThreadCallBackHandler;
import com.apestech.framework.run.core.ParamTask;
import com.apestech.framework.run.core.SingleThreadRun;
import com.apestech.framework.stream.callback.ReadRowHander;
import com.apestech.framework.util.FileUtil;
import com.apestech.framework.util.MathUtil;
import com.apestech.framework.util.StringUtil;

public class WriteArrayStream extends StringUtil{
	
	//获取全部数据为导出场景，不走该模式
	public enum RunWriteFileType {
		FIRST(0),  //只取首批数据first，前端onChange 
		MAX(1),    //获取最大范围数据max，前端读取批量数据用于制单界面下的数据加工，比如结算中获取明细数据场景
		MAX_FILE(2),  //获取最大范围数据max，走内存方式存储，一次操作完成一次清整
		FILE(3);    //全部数据存储为文件
	      
	    int value;  
	      
	    RunWriteFileType(int value) {  
	        this.value = value;  
	    }  
	      
	    public int getValue() {  
	        return value;  
	    }  
	}
	
	private ArrayObject list;
	private ByteArrayOutputStream os;
	private ByteArrayOutputStream oos;
	private ListStream file=null;
	private int fetchSize=0;
	private long maxSize=0;
	private long textSize=0;
	private LogSaveType saveType=LogSaveType.COUNT;
	private String keyField=null;
	private String pinyinField=null;
	private int keyLength=1;
	private boolean hasData=false;
	private String fileName=null; 
	private boolean synchronize=false;
	private long maxFileSize=0;
	private RunWriteFileType runType=RunWriteFileType.FIRST;
	private WebStream ws=null;
	
	public WriteArrayStream(){
		this.os=new ByteArrayOutputStream();
	}
	
	public WriteArrayStream(Object obj) throws Exception{
		this.list=new ArrayObject(obj);
		this.os=new ByteArrayOutputStream();
		this.oos=new ByteArrayOutputStream();
	}
	
	public WriteArrayStream(OutputStream os, Object obj, int fetchSize) throws Exception{
		this.list=new ArrayObject(obj);
		this.fetchSize=fetchSize;
		this.os=new ByteArrayOutputStream();
	}
	
	public void setRunType(RunWriteFileType runType){
		this.runType=runType;
	}
	
	public void setSynchronize(boolean synchronize){
		this.synchronize=synchronize;
	}
	
	public void setFetchSize(int fetchSize){
		this.fetchSize=fetchSize;
	}
	
	public void setMaxSize(long maxSize){
		this.maxSize=maxSize;
	}
	
	public void setMaxFileSize(long maxFileSize){
		this.maxFileSize=maxFileSize;
	}
	
	public void setFieldName(String fileName){
		this.fileName=fileName;
	}
	            
	public void setSaveType(LogSaveType saveType){
		this.saveType=saveType;
		if(this.file!=null){
			this.file.setSaveType(saveType);
		}
	}
	
	public void setKeyField(String keyField){
		this.keyField=keyField;
		if(this.file!=null){
			this.file.setKeyField(keyField);
		}
	}
	
	public void setPinyinField(String pinyinField){
		this.pinyinField=pinyinField;
		if(this.file!=null){
			this.file.setPinyinField(pinyinField);
		}
	}
	
	public void setKeyLength(int keyLength){
		this.keyLength=keyLength;
		if(this.file!=null){
			this.file.setKeyLength(keyLength);
		}
	}
	
	private void init() throws Exception {
		this.os.write("[".getBytes("UTF-8"));
	}

	private void fini() throws Exception {
		this.os.write("]".getBytes("UTF-8"));
	}
	
	private void processRow(ReadRowHander readRowHander) throws Exception{
		if(this.runType==RunWriteFileType.FILE){
			writeKey(readRowHander); 
		} else {
			while(this.list.next()){
				boolean flag=writeFirstLine(readRowHander);  //获取首批传回前端的数据流
				if(!flag){
					int size=this.list.getIndex()+1;
					this.fetchSize=size;
					if(this.runType==RunWriteFileType.MAX_FILE){
						ListStream file=null;
						if(this.fileName==null){
							file=new ListStream(size, this.saveType);
						} else {
							file=new ListStream(this.fileName, size, this.saveType);
						}
						file.setKeyField(this.keyField);
						file.setKeyLength(this.keyLength);
						file.setPinyinField(this.pinyinField);
						this.file=file;
						writeFirstFile();
						writeOtherData(file, readRowHander);
					} else if(this.runType==RunWriteFileType.MAX){
						ws=null;
						ws=new WebStream(this.list);
						ws.setFetchSize(size);
						ws.setCount(size);
						ws.setFirstData(this.os.toByteArray());
						writeOtherData(file, readRowHander);
					}
					break;
				}
			}
		}
		
	}
	
	private boolean writeFirstLine(ReadRowHander readRowHander) throws Exception{
		String text=readRowHander.processRow(this.list.getIndex());
		boolean flag=true;
		if(this.maxSize>0){
			this.textSize=this.textSize+text.getBytes().length;
			if(this.maxSize<this.textSize){
				flag=false;
			}
		} else {
			if(this.fetchSize==this.list.getIndex()+1){
				flag=true;
			}
		}
		this.oos.write((text+"\n").getBytes());
		this.os.write((text+"\n").getBytes("UTF-8"));  
		return flag;
	}
	
	private void writeKey(ReadRowHander readRowHander) throws Exception{  
		//以key的方式写文件，全部都为文件模式，如需要与ＷＥＢ模式对接，需要执行文件读写之后再从文件中读取
		ListStream file=null;
		if(this.fileName==null){
			file=new ListStream(this.fetchSize, this.saveType);
		} else {
			file=new ListStream(this.fileName, this.fetchSize, this.saveType);
		}
		file.setKeyField(this.keyField);
		file.setKeyLength(this.keyLength);
		file.setPinyinField(this.pinyinField);
		this.file=file;
		writeOtherData(file, readRowHander);
	}
	
	private void writeOtherData(final ListStream file, final ReadRowHander readRowHander) throws Exception{
		this.hasData=true;
		//增加写最大记录数接口
		//启用该场景要求，必须前端获取完全部最大数据，取完同时，会删除对应文件信息，否则每隔５分钟系统将自动删除，建议前端直接增加访问全部的逻辑
		//最后一个场景测试，相同ＳＱＬ及查询条件查询在每隔５分钟之内是从缓存中获取数据，这样要求对文件名及ＳＱＬ缓存有命名规则要求
		//主体调用内容根据功能拆分成小零件，之后再组合调用
		if(this.synchronize){
			fileWrite(readRowHander);
		} else {
			SingleThreadRun run=new SingleThreadRun("生成临时流数据", new ThreadCallBackHandler(){
				public void run() {  //String threadName
					try {
						fileWrite(readRowHander);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}, true);
			run.setRunClose();
			run.start();
		}
	}
	
	private void fileWrite(ReadRowHander readRowHander) throws Exception{
		int maxRowCount=-1;
		if(this.maxFileSize>0){
			maxRowCount=(int) MathUtil.ceil((this.maxFileSize*this.fetchSize)/this.maxSize, 0)-this.fetchSize;
		}
		if(this.runType==RunWriteFileType.MAX_FILE){
			final ArrayObject olist=this.list;
			file.write(olist, readRowHander, maxRowCount);
			file.close();
		} else {
			ws.setMaxCount(maxRowCount);
			//ws.write();
			ws.writeThread();
		}
		this.list.close();
	}
	
	private void writeFirstFile() throws Exception{
		if(this.file==null) return;
		String sfile="";
		sfile=this.file.getFileName()+"/temp.txt";
		File tfile=FileUtil.newFile(sfile);
		OutputStream out = new FileOutputStream(tfile);
		this.os.flush();
		this.oos.flush();
		byte[] b=this.oos.toByteArray();
		out.write(b,0,b.length); 
		out.flush();
		out.close();
		BufferedReader read = new BufferedReader(new FileReader(tfile));
		String text=read.readLine();
		while(text!=null){
			if(this.leftStr(text, 1).equals(",")){
				text=this.rightStr(text, text.length()-1);
			}
			this.file.add(text);
			text=read.readLine();
		}
		this.oos.close();
		read.close();
		tfile.delete();
		read=null;
		tfile=null;
	}
	
	public void write(ReadRowHander readRowHander) throws Exception {
		init();
		processRow(readRowHander);
		fini();
	}
	
	public void read(int page, ReadBufferFileCallBackHandler readBufferFileCallBackHandler) throws Exception{
		if(this.file==null){
			this.file=new ListStream(this.fileName);
		}
		String sfile=this.file.getFileName()+"/"+(page-1)+".txt";
		while(!FileUtil.fileExists(sfile)){}
		BufferFileRead read=new BufferFileRead(sfile);
		int i=(page-1)*this.fetchSize;
		boolean first=true;
		while(read.next()){
			i++;
			String text=read.getText();
			if(",".equals(this.leftStr(text, 1))){
				text=this.rightStr(text, text.length()-1);
			}
			readBufferFileCallBackHandler.read(text, i);
		}
	}
	
	public void read(int page) throws Exception{
		this.os=new ByteArrayOutputStream();
        if(this.runType==RunWriteFileType.MAX_FILE){
        	fileRead(page);
		} else if(this.runType==RunWriteFileType.MAX){
			byte[] b=this.ws.getData(page);
			this.os.write(b,0,b.length);
		}
	}
	
	private void fileRead(int page) throws Exception{
		if(this.file==null){
			this.file=new ListStream(this.fileName);
		}
		this.os.write("[".getBytes("UTF-8"));
		String sfile=this.file.getFileName()+"/"+(page-1)+".txt";
		while(!FileUtil.fileExists(sfile)){}
		BufferFileRead read=new BufferFileRead(sfile);
		int i=(page-1)*this.fetchSize;
		boolean first=true;
		while(read.next()){
			i++;
			String text=read.getText();
			if(",".equals(this.leftStr(text, 1))){
				text=this.rightStr(text, text.length()-1);
			}
			if(!first){
				os.write(",".getBytes("UTF-8"));
			} else {
				first=false;
			}
			os.write(text.getBytes("UTF-8"));
		}
		this.os.write("]".getBytes("UTF-8"));
		read.close();
	}
	
	public String getFileName(){
		if(this.file==null){
			return this.getUUID();
		}
		return this.file.getFileName();
	}

	public String getFile(){
		if(this.file==null){
			return getUUID();
		}
		return FileUtil.getFileName(this.file.getFileName());
	}

	
	public int Size() throws Exception{
		if(this.file==null) return 0;
		return this.file.size();
	}
	
	public ArrayList<String> getKeyList(){
		if(this.file==null) return null;
		return this.file.getKeyList();
	}
	
	/*public String toString(){
		return this.file.toString();
	}*/
	
	public String getFileIndexString(int index){
		return this.file.getFileIndexString(index);
	}
	
	public void writeOutPut(OutputStream out, JSONObject obj) throws Exception{
		byte[] b=this.os.toByteArray();
		JSONSerialization json=new JSONSerialization(obj);
		json.add("row", b);
		json.fini();
		byte[] b1=json.getBytes();
		out.write(b1, 0, b1.length);
	}
	
	public boolean hasData(){
		return this.hasData;
	}
	
	public void close() throws Exception{
		if(this.file!=null){
			this.file.close();
		}
		if(this.ws!=null){
			this.ws.close();
		}
		this.os.close();
	}
	
	public int getFileCount(){
		if(this.runType==RunWriteFileType.MAX_FILE){
			return this.file.getFileCount();
		} else {
			if(this.ws!=null){
				return this.ws.getPageCount();
			}	
		}
		return 0;
	}

	public boolean finish(){
		boolean flag=true;
		if(this.ws!=null){
			return this.ws.finish();
		}
		return flag;
	}

	public String toString(){
		return "page:"+this.getFileCount()+" finish:"+this.finish();
	}

}
